浏览器:渲染流程
当请求结果的content-type=text/html
时,浏览器就会将资源交给渲染引擎进行渲染,大概流程如下:
一、解析HTML
浏览器接收到的HTML是字节流的形式,首先会对其进行词法分析,将其转换为一个个的token
,分为Tag Token
和文本 Token
,而,Tag token
又分为StartTag
和EndTag
HTML 解析器维护了一个Token 栈结构,该 Token 栈主要用来计算节点之间的父子关系,在第一个阶段中生成的 Token 会被按照顺序压到这个栈中。
举个例子:
1 | <html> |
解析为:
二、构建DOM树
在解析的时候,就开始构建DOM树,步骤如下:
- 如果压入到栈中的是
StartTag Token
,HTML 解析器会为该 Token 创建一个 DOM 节点,然后将该节点加入到 DOM 树中,它的父节点就是栈中相邻的那个元素生成的节点。 - 如果分词器解析出来是文本 Token,那么会生成一个文本节点,然后将该节点加入到 DOM 树中,文本 Token 是不需要压入到栈中,它的父节点就是当前栈顶 Token 所对应的 DOM 节点。
- 如果分词器解析出来的是
EndTag
标签,比如是EndTag
div,HTML 解析器会查看 Token 栈顶的元素是否是StarTag
div,如果是,就将StartTag
div 从栈中弹出,表示该 div 元素解析完成。
通过分词器产生的新 Token 就这样不停地压栈和出栈,整个解析过程就这样一直持续下去,直到分词器将所有字节流分词完成。
三、构建CSS规则树
把CSS转换为浏览器能够理解的结构
浏览器同样不能识别CSS格式的文档,需要将其转换为如下一样的格式:
可以控制台中输入
document.styleSheets
就可以看到当前网页的所有样式了转换样式表中的属性值,使其标准化
CSS文本中有很多属性值,如2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。
其中像em、rem、%等大小单位,都会通过计算转换为px,而颜色会转换为对应的rgb
计算出DOM树中每个节点的具体样式
在计算每个节点的具体样式的时候,需要注意样式的
继承
和层叠
规则继承
CSS中有很多样式时可以继承的比如
font-size
,举个例子:我们给
boby
设置一个font-size:20px
的样式,那么它的所有子元素都会具有这么一个样式层叠
层叠是CSS的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。
四、生成布局树
布局树只包括可见的节点,会忽略不可见的节点,比如设置了display:none;
样式的节点
并且计算出每个节点具体的坐标位置,以便后面绘制时使用
五、分层
一些复杂的3D变换、页面滚动,或者使用z-indexing做z轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
拥有层叠上下文属性提升为单独的一个图层:明确定位属性的元素、定义透明属性的元素、使用CSS滤镜的元素等,都拥有层叠上下文属性。
需要剪裁(clip)的地方也会被创建为图层:把div的大小限定为200 * 200像素,而div里面的文字内容比较多,文字所显示的区域肯定会超出200 * 200的面积,这时候就产生了剪裁,渲染引擎会把裁剪文字内容的一部分用于显示在div区域,出现这种裁剪情况的时候,渲染引擎会为文字部分单独创建一个层,如果出现滚动条,滚动条也会被提升为单独的层。
六、图层绘制
在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制
渲染引擎实现图层的绘制与之类似,会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表
比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在图层绘制阶段,输出的内容就是这些待绘制列表。
你也可以打开“开发者工具”的“Layers”标签,选择“document”层,来实际体验下绘制列表
七、栅格化(raster)操作
当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程
所谓栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的
通常,栅格化过程都会使用GPU来加速生成,使用GPU生成位图的过程叫快速栅格化,或者GPU栅格化,生成的位图被保存在GPU内存中。
八、合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。
浏览器进程里面有一个叫viz的组件,用来接收合成线程发过来的DrawQuad命令,然后根据DrawQuad命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
总结流程:
- 渲染进程将HTML内容转换为能够读懂的DOM树结构。
- 渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets,计算出DOM节点的样式。
- 创建布局树,并计算元素的布局信息。
- 对布局树进行分层,并生成分层树。
- 为每个图层生成绘制列表,并将其提交到合成线程。
- 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
- 合成线程发送绘制图块命令DrawQuad给浏览器进程。
- 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上
重绘和重排的区别?
答:重排需要走完整个渲染流程,重绘不需要走布局流程(生成布局树、分层)